//
//  JTRunAlertPanelController.m
//  JTRunAlertPanel
//
//

/*
 * Copyright (c) 2010 Joel Tucci
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the project's author nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#import "JTRunAlertPanelController.h"

@implementation JTRunAlertPanelController

@synthesize returnValue;


-(id) init {
    if(![super init]) {
        NSLog(@"Could not init alert panel object!");
        return nil;
    }
    
    returnValue=-2;
    
    
    return self;
}

/**
 *Initializes the alert to call back methods with the given object name
 */
-(id)initWithCaller:(NSObject *)callbackObject selectorList:(NSArray *)selList 
	runOnMainThread:(BOOL)runOnMainThread {
    
    if(![super init]) {
        NSLog(@"Could not init alert panel object!");
        return nil;
    }

    selectorList=[selList retain];
    caller=[callbackObject retain];
    
    mainThreadMode=runOnMainThread;
    
    return self;

}




-(void)dealloc {
    
    [caller release];
    [selectorList release];    
    [super dealloc];
}



/*
 * Takes a list of arguments with their associated selectors(arg,sel,arg,sel), the selectors being strings which are converted
 * into selectors when they are called.
 * NOTE: the lengths of the two arrays don't have to match.
 * If the button list is longer, all buttons without matches use the same callback(the last one provided)
 * If the selector list is longer then we just ignore the extras
 * Also note that it is assumed none of these selectors take any arguments(it wouldn't make sense in the context of the original way of doing things anyway), if you pass in something with a ":" at the end, it will be IGNORED and no callback will occur
 */
void JTRunAlertPanelWithCallbacks(BOOL runOnMainThread, NSObject *callbackObject,NSString *title, 
								  NSString *alertMsg,NSArray *buttonList, NSArray *callbackList) {

    if([buttonList count] == 0 || [callbackList count] ==0) {
        NSLog(@"illegal arguments provided(no callback or button titles, bailing!");
    }
    
    JTRunAlertPanelController *controller=[[JTRunAlertPanelController alloc] initWithCaller:callbackObject 
																			   selectorList:callbackList runOnMainThread:runOnMainThread];
    
    UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:title message:alertMsg delegate:controller 
											cancelButtonTitle:[buttonList objectAtIndex:0] otherButtonTitles:nil]; 

    for(int i=1;i<[buttonList count];i++) {
        [alertView addButtonWithTitle:[buttonList objectAtIndex:i]];
    }
    
    //Must run UIAlertViews on main thread
    [alertView performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:YES];

    [alertView release];

    
}

/**
 * Runs a modal alert panel, if run from the main thread it will use NSRunLoop runUntil
 * method to wait, otherwise it sleeps
 */

NSInteger JTRunAlertPanel(NSString *title, NSString *alertMsg,NSString *cancelButtonTitle,...) {
    
    va_list argumentList;
    va_start(argumentList, cancelButtonTitle);

    
    NSMutableArray *buttonList=[[NSMutableArray alloc] init];
    id curTitle;
    
    while(curTitle=va_arg(argumentList, NSString *)) {
        [buttonList addObject:curTitle];
    }
    va_end(argumentList);
    
    JTRunAlertPanelController *controller=[[JTRunAlertPanelController alloc] init];
    UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:title message:alertMsg delegate:controller cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil]; 

    //Unfortunately since it doesn't really seem possible to pass varargs between functions
    //We are stuck with this "hack"
    
    //Enumerators in Cocoa need to be LESS verbose than this method :P
    for(int i=0;i<[buttonList count];i++) {
        
        [alertView addButtonWithTitle:[buttonList objectAtIndex:i]];
    }
    [buttonList release];
    
    //Must run UIAlertPanels on main thread
    [alertView performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:YES];
    
    //On macs alert panels run with NSRunAlertPanel are modal, since this behavior isn't definable on the iphone
    //we have to simulate it, ie freezing the current thread until the user clicks on a button
    while(controller.returnValue == -2) {
        
        //If this is the main thread, do not sleep as this will lock everything up
        //Use the run loop instead
        //NOTE: it is HIGHLY recommended you don't run this on the main loop
        if([NSThread isMainThread])
            [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeInterval:.025 sinceDate:[NSDate date]]];
        else 
            [NSThread sleepForTimeInterval:.1];
    }
    
    [alertView release];
    [controller release];
    
    int returnVal=controller.returnValue;
        
    return returnVal;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if(selectorList == nil) { //We just are doing the int return
        returnValue=buttonIndex;
        return;
    }
    
    
    int selIndex=buttonIndex;
    
    //Everything beyond the list provided gets set to the same
    if(selIndex >= [selectorList count]) 
        selIndex=[selectorList count]-1;
    
    if([[selectorList objectAtIndex:selIndex] hasSuffix:@":"]) {
        NSLog(@"ILLEGAL SELECTOR PROVIDED, ONLY PROVIDE SELECTORS THAT DO NOT TAKE ARGUMENTS!");
        return;
    }
    
	NSString *selectorName=[selectorList objectAtIndex:selIndex];
	
	//Providing a "" does a no-op
    if(![selectorName isEqualToString:@""]) {
		
		SEL curSel=NSSelectorFromString(selectorName);
    
		if(!mainThreadMode)
			[caller performSelector:curSel];
		else
			[caller performSelectorOnMainThread:curSel withObject:nil waitUntilDone:NO];
    }
	
    //A bit strange but essentially calling something as a delegate apparently doesn't retain it...
    [self release];
}


@end
